package com.jerry.security.config;

import com.jerry.security.domain.Permission;
import com.jerry.security.handler.MyAuthenticationFailureHandler;
import com.jerry.security.handler.MyAuthenticationSuccessHandler;
import com.jerry.security.mapper.PermissionMapper;
import com.jerry.security.service.MyUserDetailsService;
import com.jerry.security.utils.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;

/**
 * // SpringBoot2.0 抛弃了原来的NoOpPasswordEncoder，要求用户保存的密码必须要使用加密算法后存储，
 * 在登录验证的时候Security会将获得的密码在进行编码后再和数据库中加密后的密码进行对比
 */
@Configuration
@EnableWebSecurity    //启动SpringSecurity过滤器链
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Autowired
    private PermissionMapper permissionMapper;

    //该方法的作用就是代替xml中的<security:authentication-manager>配置
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService).passwordEncoder(new PasswordEncoder() {
            // 加密的密码与数据库密码进行比对。rawPassword 代表表单字段，encodedPassword 代表数据库加密字段
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                System.out.println("rawPassword:" + rawPassword + ",encodedPassword:" + encodedPassword);
                // 返回true 表示认证成功，返回fasle 表示认证失败
                return MD5Util.encode((String) rawPassword).equals(encodedPassword);
            }

            // 对表单密码进行加密
            public String encode(CharSequence rawPassword) {
                System.out.println("rawPassword:" + rawPassword);
                return MD5Util.encode((String) rawPassword);
            }
        });
    }

    //该方法的作用就是代替xml中的<security:http>配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http
                .authorizeRequests();
        // 1.读取数据库权限列表
        List<Permission> listPermission = permissionMapper.findAllPermission();
        for (Permission permission : listPermission) {
            // 设置权限
            authorizeRequests.antMatchers(permission.getUrl()).hasAnyAuthority(permission.getPermTag());
        }
        authorizeRequests
                .antMatchers("/product/index").permitAll()  //访问/product/index时，不需要任何权限
                .antMatchers("/js/**").permitAll()  //访问js文件时，不需要任何权限
                .antMatchers("/css/**").permitAll()  //访问css文件时，不需要任何权限
                .antMatchers("/").permitAll()  //所有用户都可以访问根目录
                .antMatchers("/customLogin").permitAll()  //所有用户都可以访问登录页面
                .anyRequest().fullyAuthenticated() //所有请求都需要认证才能访问
                .and()
                .logout().permitAll()  //所有用户都可以退出
                .and()
                .formLogin().loginPage("/customLogin").loginProcessingUrl("/securityLogin").successForwardUrl("/product/index")     //自定义表单登录
                .successHandler(myAuthenticationSuccessHandler).failureHandler(myAuthenticationFailureHandler)   //成功和失败的handler
                .and()
                .csrf().disable();   //关闭Spring Security CSRF机制
    }


}
